package github.kawaiior.yuanchuxiuzhen.common.capability.entity;

import github.kawaiior.yuanchuxiuzhen.Yuanchuxiuzhen;
import github.kawaiior.yuanchuxiuzhen.common.capability.IReplicableCap;
import github.kawaiior.yuanchuxiuzhen.common.gongfa.IGongFa;
import github.kawaiior.yuanchuxiuzhen.core.init.GongFaInit;
import github.kawaiior.yuanchuxiuzhen.core.init.event.ModPlayerEvent;
import github.kawaiior.yuanchuxiuzhen.core.registry.ModCapability;
import github.kawaiior.yuanchuxiuzhen.network.NetworkRegistryHandler;
import github.kawaiior.yuanchuxiuzhen.network.packet.UpdateManaPacket;
import github.kawaiior.yuanchuxiuzhen.util.ManaPowerHelper;
import github.kawaiior.yuanchuxiuzhen.util.MathUtil;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance;
import net.minecraft.entity.effect.LightningBoltEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.network.PacketDistributor;

import javax.annotation.Nullable;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 重构代码
 */
public class ManaPower implements INBTSerializable<CompoundNBT>, IReplicableCap {

    public static final boolean showManaAddMessage = false;

    public ManaPower(int level, int innerLevel, int experience,
                     float health, float mana, float maxMana, float attack, float defense, float manaRecovery,
                     float merit, int thunderTribulation, float evilSpirit,
                     int refining, int alchemy, int character, int fragment, boolean vitality,
                     byte[] manaRootArray, byte[] gongFaArray, int learningGongFa, int gongFaExp) {
        this.level = level;
        this.innerLevel = innerLevel;
        this.experience = experience;
        this.health = health;
        this.mana = mana;
        this.maxMana = maxMana;
        this.attack = attack;
        this.defense = defense;
        this.manaRecovery = manaRecovery;
        this.merit = merit;
        this.thunderTribulation = thunderTribulation;
        this.evilSpirit = evilSpirit;
        this.refining = refining;
        this.alchemy = alchemy;
        this.character = character;
        this.fragment = fragment;
        this.vitality = vitality;
        this.manaRootArray = manaRootArray;
        this.gongFaArray = gongFaArray;
        this.learningGongFa = learningGongFa;
        this.gongFaExp = gongFaExp;
    }

    /**
     * 初始化灵根
     */
    private void initManaRoot(){
        boolean flag = true;
        // 每个灵根都有40%概率获得，概率相互独立
        for (int i = 0; i < this.manaRootArray.length; i++) {
            if (MathUtil.checkProbability(0.4F)){
                this.manaRootArray[i] = 1;
                flag = false;
            }
        }
        // 保底机制，玩家至少拥有一个灵根
        if (flag){
            this.manaRootArray[MathUtil.RANDOM.nextInt(5)] = 1;
        }
    }

    public ManaPower(){
        initManaRoot();
    }

    /**
     * 等级
     */
    // 大等级
    private int level = 0;
    // 小等级
    private int innerLevel = 1;
    // 经验值
    private int experience = 0;

    /**
     * 增加经验值
     */
    public void addExperience(int experience, World world, PlayerEntity player){
        if (showManaAddMessage){
            player.sendMessage(new StringTextComponent("获得"+experience+"点灵气"), player.getUniqueID());
        }

        addExperience(experience, world);
        sendToClient(player, world);
    }

    private void addExperience(int experience, World world){
        int maxExperience = ManaPowerHelper.getMaxExperience(level, innerLevel);
        if (this.level == ManaPowerHelper.MAX_LEVEL && this.innerLevel == ManaPowerHelper.MAX_INNER_LEVEL){
            if (this.experience == maxExperience){
                return;
            }
            this.experience += experience;
            if (this.experience >= maxExperience){
                this.experience = maxExperience;
                finalPowerBoost();
            }
            return;
        }
        this.experience += experience;
        if (this.experience >= maxExperience){
            this.innerLevelUp(world);
            int last = this.experience - maxExperience;
            this.experience = 0;
            this.addExperience(last, world);
        }
    }

    private void powerBoost(){
        // this.health += ManaPowerHelper.getHealthBoost(level, innerLevel);
        this.addHealth(ManaPowerHelper.getHealthBoost(level, innerLevel));

        this.maxMana += ManaPowerHelper.getMaxManaBoost(level, innerLevel);
        this.manaRecovery += ManaPowerHelper.getManaRecoveryBoost(level, innerLevel);

        // this.attack += ManaPowerHelper.getAttackBoost(level, innerLevel);
        this.addAttack(ManaPowerHelper.getAttackBoost(level, innerLevel));

        this.defense += ManaPowerHelper.getDefenseBoost(level, innerLevel);
    }

    private void finalPowerBoost(){
        //this.health += 100;
        this.addHealth(100);

        this.maxMana += 50000;
        this.manaRecovery += 10;

        // this.attack += 20;
        this.addAttack(20);

        this.defense += 20;
    }

    public void innerLevelUp(World world){
        updateAverageLevel(world);

        if (this.level == 0){
            this.level = 1;
            this.innerLevel = 1;
            powerBoost();
            return;
        }

        this.innerLevel += 1;
        if (this.innerLevel > ManaPowerHelper.MAX_INNER_LEVEL){
            if (this.level < ManaPowerHelper.MAX_LEVEL){
                this.innerLevel = 1;
                this.level += 1;
                // 元婴以上渡劫
                if (this.level > 4){
                    this.addThunderTribulation(5 * this.level + ((int)(this.evilSpirit / 30)));
                }
            }else {
                this.innerLevel = ManaPowerHelper.MAX_INNER_LEVEL;
                return;
            }
        }
        powerBoost();
    }

    /**
     * 属性
     */
    // 生命值
    private float health = 20F;
    // MP
    private float mana = 0F;
    // MaxMP
    private float maxMana = 1000F;
    // 攻击力
    private float attack = 1F;
    // 防御力
    private float defense = 0F;
    // MP回复（每秒）
    private float manaRecovery = 1F;

    /**
     * 消耗mana
     */
    public boolean consumeMana(PlayerEntity player, World world, float amount){
        if (this.mana < amount){
            return false;
        }
        this.mana -= amount;
        sendToClient(player, world);
        return true;
    }

    /**
     * 返回消耗的mana
     */
    public float consumeManaAnyway(PlayerEntity player, World world, float amount){
        if (this.mana > amount){
            this.mana -= amount;
        }else {
            amount = this.mana;
            this.mana = 0;
        }
        sendToClient(player, world);
        return amount;
    }

    /**
     * 增加mana
     */
    public void addMana(float amount, PlayerEntity player, World world){
        this.mana += amount;
        if (this.mana > this.getMaxMana()){
            this.mana = this.getMaxMana();
        }
        sendToClient(player, world);
    }

    public void addMana(float amount, PlayerEntity player, World world, boolean send){
        this.mana += amount;
        if (this.mana > this.getMaxMana()){
            this.mana = this.getMaxMana();
        }
        if (send){
            sendToClient(player, world);
        }
    }

    public void addHealth(float amount) {
        this.health += amount;
        if (this.health > ManaPowerHelper.MAX_HEALTH){
            this.health = ManaPowerHelper.MAX_HEALTH;
        }
    }

    public void addMaxMana(float amount) {
        this.maxMana += amount;
    }

    public void addAttack(float amount) {
        this.attack += amount;
        if (this.attack > ManaPowerHelper.MAX_ATTACK){
            this.attack = ManaPowerHelper.MAX_ATTACK;
        }
    }

    public void addDefense(float amount) {
        this.defense += amount;
    }

    public void addManaRecovery(float amount) {
        this.manaRecovery += amount;
    }

    /**
     * 特殊属性
     */
    // 功德
    private float merit = 0F;
    // 雷劫
    private int thunderTribulation = 0;
    // 煞气
    private float evilSpirit = 0F;

    public void addMerit(World world, PlayerEntity player, int amount){
        if (showManaAddMessage){
            player.sendMessage(new StringTextComponent("获得"+amount+"点功德"), player.getUniqueID());
        }

        this.merit += amount;
        this.sendToClient(player, world);
    }

    public void consumeMerit(World world, PlayerEntity player, int amount){
        if (showManaAddMessage){
            player.sendMessage(new StringTextComponent("减少"+amount+"点功德"), player.getUniqueID());
        }

        this.merit -= amount;
        this.sendToClient(player, world);
    }

    public void addEvilSpirit(World world, PlayerEntity player, int amount){
        if (showManaAddMessage){
            player.sendMessage(new StringTextComponent("获得"+amount+"点煞气"), player.getUniqueID());
        }

        this.evilSpirit += amount;
        this.sendToClient(player, world);
    }

    /**
     * 增加雷劫数量
     */
    public void addThunderTribulation(int amount){
        this.thunderTribulation += amount;
    }

    /**
     * 消耗雷劫数
     */
    public boolean consumeThunderTribulation(){
        if (this.thunderTribulation > 0){
            this.thunderTribulation -= 1;
            return true;
        }
        return false;
    }

    public boolean consumeThunderTribulation(int amount){
        if (this.thunderTribulation >= amount){
            this.thunderTribulation -= amount;
            return true;
        }
        return false;
    }

    /**
     * 天赋
     */
    // 炼器
    private int refining = 0;
    // 炼丹
    private int alchemy = 0;
    // 画符
    private int character = 0;
    // 支离
    private int fragment = 0;

    /**
     * 提升支离
     */
    public void addFragment(World world, PlayerEntity player, int amount) {
        if (showManaAddMessage){
            player.sendMessage(new StringTextComponent("获得"+amount+"点支离经验"), player.getUniqueID());
        }

        this.fragment += amount;
        this.sendToClient(player, world);
    }

    /**
     * 其他属性
     */
    private boolean vitality = false;
    // 金木水火土
    private byte[] manaRootArray = new byte[]{0, 0, 0, 0, 0};
    // 功法
    private byte[] gongFaArray = new byte[GongFaInit.GONG_FAS.size()];
    // 正在学习的功法 -1表示空
    private int learningGongFa = -1;
    // 功法经验值
    private int gongFaExp = 0;

    /**
     * vitality
     */
    public boolean isVitality(){
        return this.vitality;
    }

    public void setVitality(boolean vitality){
        this.vitality = vitality;
    }

    /**
     * 是否具有某个灵根
     */
    public boolean hasManaRoot(ManaRoot manaRoot){
        return manaRoot.getValue() == 5 || this.manaRootArray[manaRoot.getValue()] == 1;
    }

    public String getManaRootText(){
        String manaRootText = "";
        if (hasManaRoot(ManaRoot.METAL)){
            manaRootText += ManaRoot.METAL.toString();
        }
        if (hasManaRoot(ManaRoot.WOOD)){
            manaRootText += ManaRoot.WOOD.toString();
        }
        if (hasManaRoot(ManaRoot.WATER)){
            manaRootText += ManaRoot.WATER.toString();
        }
        if (hasManaRoot(ManaRoot.FIRE)){
            manaRootText += ManaRoot.FIRE.toString();
        }
        if (hasManaRoot(ManaRoot.EARTH)){
            manaRootText += ManaRoot.EARTH.toString();
        }

        return manaRootText;
    }

//    public String getManaRootText(){
//        String manaRootText = "";
//        if (hasManaRoot(ManaRoot.METAL)){
//            manaRootText += "金";
//        }
//        if (hasManaRoot(ManaRoot.WOOD)){
//            manaRootText += "木";
//        }
//        if (hasManaRoot(ManaRoot.WATER)){
//            manaRootText += "水";
//        }
//        if (hasManaRoot(ManaRoot.FIRE)){
//            manaRootText += "火";
//        }
//        if (hasManaRoot(ManaRoot.EARTH)){
//            manaRootText += "土";
//        }
//
//        return manaRootText;
//    }

    /**
     * 判断是否已经学习了某个功法
     */
    public boolean isLearnedGongFa(IGongFa gongFa){
        return this.gongFaArray[GongFaInit.getGongFaIndex(gongFa)] == 1;
    }

    /**
     * 获取正在学习的功法
     */
    @Nullable
    public IGongFa getLearningGongFaObj(){
        if (this.learningGongFa == -1){
            return null;
        }
        return GongFaInit.getGongFa(this.learningGongFa);
    }

    /**
     * 判断正在学习的功法
     */
    public boolean isLearningGongFa(IGongFa gongFa){
        return getLearningGongFaObj() == gongFa;
    }

    /**
     * 是否可以学习某个功法
     */
    public boolean canLearnGongFa(IGongFa gongFa){
        IGongFa preGongFa = gongFa.getPreGongFa();
        return
                hasManaRoot(gongFa.getManaRoot()) &&
                (preGongFa == GongFaInit.EMPTY_GONG_FA || isLearnedGongFa(preGongFa)) &&
                this.learningGongFa==-1 &&
                !isLearnedGongFa(gongFa);
    }

    public LearnGongFanEnum getGongFaEnum(IGongFa gongFa){
        IGongFa preGongFa = gongFa.getPreGongFa();

        if (!hasManaRoot(gongFa.getManaRoot())) return LearnGongFanEnum.NON_MANA_ROOT;

        if (!(preGongFa == GongFaInit.EMPTY_GONG_FA || isLearnedGongFa(preGongFa))) {
            return LearnGongFanEnum.NON_PRE;
        }

        if (this.learningGongFa!=-1){
            if (this.learningGongFa==GongFaInit.getGongFaIndex(gongFa)){
                return LearnGongFanEnum.LEARNING;
            }
            return LearnGongFanEnum.LEARNING_OTHER;
        }

        if (this.isLearnedGongFa(gongFa)) return LearnGongFanEnum.LEARNED;

        return LearnGongFanEnum.CAN;
    }

    public enum LearnGongFanEnum{
        CAN,
        NON_PRE,
        LEARNED,
        LEARNING,
        LEARNING_OTHER,
        NON_MANA_ROOT;
    }

    /**
     * 开始学习功法
     */
    public void setLearningGongFa(IGongFa gongFa){
        this.learningGongFa = GongFaInit.getGongFaIndex(gongFa);
    }

    /**
     * 标记功法已学习
     */
    private void setGongFaLearned(IGongFa gongFa){
        this.gongFaArray[GongFaInit.getGongFaIndex(gongFa)] = 1;
        this.learningGongFa = -1;
    }

    /**
     * 功法经验值
     */
    public void addGongFaExp(PlayerEntity player, World world, int experience){
        if (this.learningGongFa==-1 || world.isRemote){
            return;
        }

        this.gongFaExp += experience;
        IGongFa learningGongFa = getLearningGongFaObj();
        if (this.gongFaExp >= learningGongFa.getMaxExperience()){
            this.setGongFaLearned(learningGongFa);

            learningGongFa.sendLearnedMessage(player);
            learningGongFa.onPlayerLearnFinish(player, world, this);
            this.gongFaExp = 0;
        }

        sendToClient(player, world);
    }

    /**
     * 通信
     */
    public void sendToClient(PlayerEntity player, World world){
        if (!world.isRemote){
            NetworkRegistryHandler.INSTANCE.send(
                    PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) player), new UpdateManaPacket(player));
        }
    }

    /**
     * player tick
     */
    public static void onPlayerTick(PlayerEntity player){
        LazyOptional<ManaPower> capability = player.getCapability(ModCapability.MANA_POWER);
        capability.ifPresent((power)->{
            // 渡劫
            if (power.getThunderTribulation() > 0){
                if (MathUtil.checkProbability(0.5F)){

                    // 下雨
                    if (!player.world.isRaining()){
                        Biome currentBiome = player.world.getBiome(new BlockPos(player.getPositionVec()));
                        if (currentBiome.getPrecipitation() == Biome.RainType.RAIN){
                            player.world.getWorldInfo().setRaining(true);
                        }
                    }

                    power.consumeThunderTribulation();
                    LightningBoltEntity lightningBolt = new LightningBoltEntity(EntityType.LIGHTNING_BOLT, player.world);
                    lightningBolt.setPosition(player.getPosX(), player.getPosY(), player.getPosZ());
                    lightningBolt.setDamage(power.getLevel() * 1.5F);
                    player.world.addEntity(lightningBolt);
                }
            }

            if (power.getMana() < power.getMaxMana()){
                float amount = Math.min(power.getMaxMana()-power.getMana(), power.getManaRecovery());
                power.addMana(amount, player, player.world, false);
            }

            power.sendToClient(player, player.world);
        });
    }

    /**
     * on player join
     */
    public static void update(PlayerEntity player){
        LazyOptional<ManaPower> capability = player.getCapability(ModCapability.MANA_POWER);

        capability.ifPresent((manaPower)-> {
            ModifiableAttributeInstance healthAttribute = player.getAttribute(Attributes.MAX_HEALTH);
            assert healthAttribute != null;
            healthAttribute.setBaseValue(manaPower.getMaxHealth());
        });
    }

    public static void update(PlayerEntity player, ManaPower power){
        ModifiableAttributeInstance healthAttribute = player.getAttribute(Attributes.MAX_HEALTH);
        assert healthAttribute != null;
        healthAttribute.setBaseValue(power.getMaxHealth());
    }

    public static void onPlayerJoinWorld(World world){
        updateAverageLevel(world);
    }

    public static void onPlayerLeave(World world){
        updateAverageLevel(world);
    }

    public static void updateAverageLevel(World world){
        List<? extends PlayerEntity> playerList = world.getPlayers();
        AtomicInteger levelCount = new AtomicInteger();
        for (int i = 0; i < playerList.size(); i++) {
            PlayerEntity player = playerList.get(i);
            LazyOptional<ManaPower> capability = player.getCapability(ModCapability.MANA_POWER);
            capability.ifPresent((power)->{
                levelCount.addAndGet(power.getLevel() * 4 + power.getInnerLevel());
            });
        }
        ModPlayerEvent.averageLevel = levelCount.get() / playerList.size();
        Yuanchuxiuzhen.debug("当前平均等级为: "+ModPlayerEvent.averageLevel);
    }

    public void setPower(IReplicableCap replicable){
        if (replicable instanceof ManaPower){
            ManaPower power = (ManaPower) replicable;

            this.level = power.getLevel();
            this.innerLevel = power.getInnerLevel();
            this.experience = power.getExperience();

            this.health = power.getMaxHealth();
            this.mana = power.getMana();
            this.maxMana = power.getMaxMana();
            this.attack = power.getAttack();
            this.defense = power.getDefense();
            this.manaRecovery = power.getManaRecovery();

            this.merit = power.getMerit();
            this.thunderTribulation = power.getThunderTribulation();
            this.evilSpirit = power.getEvilSpirit();

            this.refining = power.getRefining();
            this.alchemy = power.getAlchemy();
            this.character = power.getCharacter();
            this.fragment = power.getFragment();

            this.vitality = power.isVitality();
            this.manaRootArray = power.getManaRootArray();
            this.gongFaArray = power.getGongFaArray();
            this.learningGongFa = power.getLearningGongFa();
            this.gongFaExp = power.getGongFaExp();
        }
    }

    @Override
    public CompoundNBT serializeNBT() {
        CompoundNBT nbt = new CompoundNBT();

        nbt.putInt("ycxz_level", this.level);
        nbt.putInt("ycxz_inner_level", this.innerLevel);
        nbt.putInt("ycxz_experience", this.experience);

        nbt.putFloat("ycxz_health", this.health);
        nbt.putFloat("ycxz_mana", this.mana);
        nbt.putFloat("ycxz_max_mana", this.maxMana);
        nbt.putFloat("ycxz_attack", this.attack);
        nbt.putFloat("ycxz_defense", this.defense);
        nbt.putFloat("ycxz_manaRecovery", this.manaRecovery);

        nbt.putFloat("ycxz_merit", this.merit);
        nbt.putInt("ycxz_thunderTribulation", this.thunderTribulation);
        nbt.putFloat("ycxz_evilSpirit", this.evilSpirit);

        nbt.putInt("ycxz_refining", this.refining);
        nbt.putInt("ycxz_alchemy", this.alchemy);
        nbt.putInt("ycxz_character", this.character);
        nbt.putInt("ycxz_fragment", this.fragment);

        nbt.putBoolean("ycxz_vitality", this.vitality);
        nbt.putByteArray("ycxz_manaRootArray", this.manaRootArray);
        nbt.putByteArray("ycxz_gongFaArray", this.gongFaArray);
        nbt.putInt("ycxz_learningGongFa", this.learningGongFa);
        nbt.putInt("ycxz_gongfa_exp", this.gongFaExp);

        return nbt;
    }

    @Override
    public void deserializeNBT(CompoundNBT nbt) {
        this.level = nbt.getInt("ycxz_level");
        this.innerLevel = nbt.getInt("ycxz_inner_level");
        this.experience = nbt.getInt("ycxz_experience");

        this.health = nbt.getFloat("ycxz_health");
        this.mana = nbt.getFloat("ycxz_mana");
        this.maxMana = nbt.getFloat("ycxz_max_mana");
        this.attack = nbt.getFloat("ycxz_attack");
        this.defense = nbt.getFloat("ycxz_defense");
        this.manaRecovery = nbt.getFloat("ycxz_manaRecovery");

        this.merit = nbt.getFloat("ycxz_merit");
        this.thunderTribulation = nbt.getInt("ycxz_thunderTribulation");
        this.evilSpirit = nbt.getFloat("ycxz_evilSpirit");

        this.refining = nbt.getInt("ycxz_refining");
        this.alchemy = nbt.getInt("ycxz_alchemy");
        this.character = nbt.getInt("ycxz_character");
        this.fragment = nbt.getInt("ycxz_fragment");

        this.vitality = nbt.getBoolean("ycxz_vitality");
        this.manaRootArray = nbt.getByteArray("ycxz_manaRootArray");
        this.gongFaArray = nbt.getByteArray("ycxz_gongFaArray");
        this.learningGongFa = nbt.getInt("ycxz_learningGongFa");
        this.gongFaExp = nbt.getInt("ycxz_gongfa_exp");
    }

    /**
     * get方法
     */
    public int getLevel(){
        return level;
    }

    public int getInnerLevel(){
        return innerLevel;
    }
    public int getExperience(){
        return experience;
    }
    public int getMaxExperience(){
        return ManaPowerHelper.getMaxExperience(this.level, this.innerLevel);
    }
    public boolean isMaxExperience(){
        return this.experience == ManaPowerHelper.getMaxExperience(this.level, this.innerLevel);
    }
    public float getHealth() {
        return health;
    }

    public float getMaxHealth(){
        return health;
    }

    public float getMana() {
        return mana;
    }

    public float getMaxMana() {
        return maxMana;
    }

    public float getAttack() {
        return attack;
    }

    public float getDefense() {
        return defense;
    }

    public float getManaRecovery() {
        return manaRecovery;
    }

    public float getMerit() {
        return merit;
    }

    public int getThunderTribulation() {
        return thunderTribulation;
    }

    public float getEvilSpirit() {
        return evilSpirit;
    }

    public int getRefining() {
        return refining;
    }

    public int getAlchemy() {
        return alchemy;
    }

    public int getCharacter() {
        return character;
    }

    public int getFragment() {
        return fragment;
    }

    public byte[] getManaRootArray() {
        return manaRootArray;
    }

    public byte[] getGongFaArray() {
        return gongFaArray;
    }

    public int getLearningGongFa(){
        return learningGongFa;
    }

    public int getGongFaExp() {
        return gongFaExp;
    }

    public enum ManaRoot{
        METAL(0),
        WOOD(1),
        WATER(2),
        FIRE(3),
        EARTH(4),
        EMPTY(5);

        private final int value;

        ManaRoot(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        @Override
        public String toString() {
            return I18n.format("ycxz.player.mana.root."+this.value);
        }
    }
}
